package com.aiyan.edu.service.auth.center.service;

import com.aiyan.edu.framework.client.AiyanServices;
import com.aiyan.edu.framework.domain.ucenter.ext.AuthToken;
import com.aiyan.edu.framework.domain.ucenter.ext.UserToken;
import com.aiyan.edu.framework.domain.ucenter.response.AuthCode;
import com.aiyan.edu.framework.domain.ucenter.response.JwtResult;
import com.aiyan.edu.framework.exception.ExceptionThrow;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: zqzhou
 * @create: 2019/11/21 21:02
 **/
@Service
public class AuthService {

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Value("${auth.tokenValidity}")
    int tokenValidity;

    /**
     * 申请令牌并缓存到redis
     * 请求spring security oauth 认证接口申请令牌
     *
     * @param username
     * @param password
     * @param clientId
     * @param clientSecret
     * @return
     */
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        AuthToken authToken = this.applyToken(username, password, clientId, clientSecret);
        if (null == authToken) {
            ExceptionThrow.out(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }

        String token_key = "user:token:" + authToken.getAccess_token();
        this.stringRedisTemplate.boundValueOps(token_key).set(JSON.toJSONString(authToken), this.tokenValidity, TimeUnit.SECONDS);
        Long expire = this.stringRedisTemplate.boundValueOps(token_key).getExpire();
        if (expire < 0) {
            ExceptionThrow.out(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
        }
        return authToken;
    }

    /**
     * 申请令牌
     *
     * @param username
     * @param password
     * @param clientId
     * @param clientSecret
     * @return
     */
    private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {

        ServiceInstance choose = this.loadBalancerClient.choose(AiyanServices.AIYANEDU_SERVICE_CENTER_AUTH);
        String path = choose.getUri().toString() + "/auth/oauth/token";

        //定义body
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.add("grant_type", "password");
        formData.add("username", username);
        formData.add("password", password);

        //base64编码
        byte[] encode = Base64Utils.encode((clientId + ":" + clientSecret).getBytes());
        MultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        header.add("Authorization", "Basic " + new String(encode));

        this.restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
                if (statusCode.value() != 400 && statusCode.value() != 401) {
                    super.handleError(response, statusCode);
                }
            }
        });

        Map map = null;
        try {
            ResponseEntity<Map> responseEntity = this.restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<>(formData, header), Map.class);
            map = responseEntity.getBody();
        } catch (RestClientException e) {
            e.printStackTrace();
            ExceptionThrow.out(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }

        if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {
            //解析spring security返回的错误信息
            if (map != null && map.get("error_description") != null) {
                String error_description = (String) map.get("error_description");
                if (error_description.indexOf("UserDetailsService returned null") >= 0) {
                    ExceptionThrow.out(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
                } else {
                    ExceptionThrow.out(AuthCode.AUTH_CREDENTIAL_ERROR);
                }
            }
        }

        AuthToken authToken = new AuthToken();
        authToken.setAccess_token(map.get("jti").toString());
        authToken.setRefresh_token(map.get("refresh_token").toString());
        authToken.setJwt_token(map.get("access_token").toString());

        return authToken;
    }

    /**
     * 根据用户令牌获取令牌
     *
     * @param utoken
     * @return
     */
    public AuthToken userJwt(String utoken) {
        String token_key = "user:token:" + utoken;
        String redisvalue = this.stringRedisTemplate.opsForValue().get(token_key);

        try {
            return JSON.parseObject(redisvalue, AuthToken.class);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 退出登录，删除redis中的令牌
     *
     * @param utoken
     */
    public boolean logout(String utoken) {
        String token_key = "user:token:" + utoken;
        return this.stringRedisTemplate.delete(token_key);
    }
}
